/******************************************************************************
 * (c) 2011 Komersi Software                                                  *
 * www.komersisoftware.com                                                    *
 *                                                                            *
 * This file is part of Tadah                                                 *
 * Licensed under GNU General Public License v2.0                             *
 * Check LICENSE.TXT for more information                                     *
 ******************************************************************************/

#include <stdlib.h>
#include <string.h>

#include "debug.h"
#include "engine.h"
#include "ddl.h"
#include "genericobject.h"
#include "rowinfo.h"

static int read_object(FILE *file, ddl_column_t *column, generic_object_t **object, 
                            void *val, size_t size, size_t count)
{
	size_t result;

	generic_object_t *obj = NULL;

	if (object == NULL)
	{
		object = malloc(sizeof(generic_object_t *));
		if (object == NULL) return FAILURE;
	}
	
	val = calloc(count, size);
	if (val == NULL) return FAILURE;
	
	obj = new_object(column);
	
	result = fread(val, size, count, file);
	
	if (result == count)
	{
		set_object_value_ptr(obj, val);
		*object = obj;
		return SUCCESS;
	}
	else
	{
		free(val);
		val = NULL;
		dispose_object(&obj);
		
		return FAILURE;
	}
}

static int read_int_object(FILE *file, ddl_column_t *column, generic_object_t **object)
{
	int *int_val = NULL;
	
	debug("Reading int column");
	return read_object(file, column, object, int_val, sizeof(int), 1);
}

static int read_char_object(FILE *file, ddl_column_t *column, generic_object_t **object)
{
	char *char_val = NULL;
	
	debug("Reading char column");
	return read_object(file, column, object, char_val, sizeof(char), column->size + 1);
}

#ifndef _MSC_VER
inline
#endif
static void set_eof(FILE *file)
{
   fclose(file);
}

row_info_t *read_table(table_info_t *table)
{
	ddl_column_t *cur = NULL;

    generic_object_t *row = NULL;
    generic_object_t *current_object = NULL;
    generic_object_t *obj = NULL;
    row_info_t *cursor = NULL;
    row_info_t *current_row = NULL;

    FILE *file = NULL;

    int cur_line = 0;
    char msg[100];

    long file_size;

    debug("Begin reading file.");
   
    file = fopen(table->table_filename, "rb");
    if (file == NULL)
	{
    	return NULL;
	}

    fseek(file , 0 , SEEK_END);
    file_size = ftell(file);
    rewind(file);

    sprintf(msg, "File size is %ld bytes", file_size);
    debug(msg);
       
    while (!feof(file))
    {
    	if (ftell(file) >= file_size)
    	{
    		break;
    	}

    	sprintf(msg, "Reading line %d", ++cur_line);
        debug(msg);

        row = NULL;
        current_row = add_new_row(&cursor, table);
        cur = table->column_list;
      
        while (cur != NULL)
        {
            current_object = add_new_object(&row, cur);
        	switch (cur->type)
            {
            	case Int:
					if (!read_int_object(file, cur, &obj))
                    {
                        set_eof(file);
                    	return NULL;
                    }
                    
                    set_object_value_int(current_object, get_object_value_int(obj));
                 	break;
            	case Char:
					if (!read_char_object(file, cur, &obj))
                    {
                        set_eof(file);
                    	return NULL;
                    }

                    set_object_value_char(current_object, get_object_value_char(obj), cur->size);
                 	break;
         	}
         	
        	dispose_object(&obj);
   			
         	cur = cur->next;
      	}

        current_row->data = row;
	}
   
	set_eof(file);

    return cursor;
}

int append_to_primary_index(table_info_t *table, ddl_column_t *column,
		generic_object_t *row, long position_in_file)
{
	generic_object_t *index_object = NULL;
	FILE *file = NULL;

	if (table == NULL)
	{
		return FAILURE;
	}

	if (column == NULL)
	{
		return FAILURE;
	}

	if (row == NULL)
	{
		return FAILURE;
	}

	file = fopen(table->primary_index_filename, "ab");
	if (file == NULL)
	{
		return FAILURE;
	}

	index_object = object_from_column(row, column);

	switch(column->type)
	{
	case Int:
		fwrite(index_object->value, sizeof(int), 1, file);
		break;
	case Char:
		fwrite(index_object->value, sizeof(char), column->size + sizeof(char), file);
		break;
	}

	fwrite(&position_in_file, sizeof(long), 1, file);

	fclose(file);

	return SUCCESS;
}

int append_to_file(table_info_t *table, generic_object_t *row)
{
    generic_object_t *obj = NULL;
    ddl_column_t *cur = NULL;
    ddl_column_t *index_column = NULL;
    long position_in_file;

    FILE *file = NULL;

    if (table == NULL)
    {
    	return FAILURE;
    }

    if (row == NULL)
    {
    	return FAILURE;
    }

    index_column = get_primary_index(table);

    // TODO : check if it will not duplicate primary index

    file = fopen(table->table_filename, "ab");
    if (file == NULL) return FAILURE;

    //check position in file to store in indexes
    fseek(file , 0 , SEEK_END);
    position_in_file = ftell(file);
    if (!append_to_primary_index(table, index_column, row, position_in_file))
    {
    	return FAILURE;
    }

    cur = table->column_list;
    while (cur != NULL)
    {
        obj = object_from_column(row, cur);
        switch (cur->type)
        {
            case Int:
                fwrite((int *) obj->value, sizeof(int), 1, file);
                break;
            case Char:
                fwrite((char *) obj->value, sizeof(char), cur->size + sizeof(char), file);
                break;
        }

        cur = cur->next;
    }

    fclose(file);
    return SUCCESS;
}

ddl_column_t *load_ddl_list(table_info_t *table)
{
    ddl_column_t *column_list = NULL;
    FILE *file = NULL;
    long file_size;

    char name[COLUMN_NAME_LENGTH + sizeof(char)];
    data_type_t type;
    int size;
    primary_index_type_t primary_index;

    int error = FALSE;

    file = fopen(table->ddl_filename, "rb");
    if (file == NULL)
    {
    	return NULL;
    }

    fseek(file , 0 , SEEK_END);
    file_size = ftell(file);
    rewind(file);

    while (!feof(file))
    {
    	if (ftell(file) >= file_size)
    	{
    		error = FALSE;
    		break;
    	}

    	memset(name, 0, COLUMN_NAME_LENGTH + sizeof(char));

    	if (fread(name, sizeof(char), COLUMN_NAME_LENGTH + sizeof(char), file) != COLUMN_NAME_LENGTH + sizeof(char))
    	{
    		error = TRUE;
    		break;
    	}

    	if (fread(&type, sizeof(data_type_t), 1, file) != 1)
    	{
    		error = TRUE;
    		break;
    	}

    	if (fread(&size, sizeof(int), 1, file) != 1)
    	{
    		error = TRUE;
    		break;
    	}

    	if (fread(&primary_index, sizeof(primary_index_type_t), 1, file) != 1)
    	{
    		error = TRUE;
    		break;
    	}

    	column_list_add(&column_list, new_ddl_column(name, type, size, primary_index));
    }

    set_eof(file);

    if (error)
    {
    	dispose_column_list(&column_list);
    	return NULL;
    }

    return column_list;
}
